package org.gdstash.db;

import org.gdstash.db.criteria.AbstractItemCombination;
import org.gdstash.db.criteria.CriteriaCombination;
import org.gdstash.db.criteria.ItemIDItemCombination;
import org.gdstash.db.criteria.ItemIDItemSetCombination;
import org.gdstash.file.*;
import org.gdstash.item.GDItem;
import org.gdstash.ui.GDStashFrame;
import org.gdstash.util.GDColor;
import org.gdstash.util.GDConstants;
import org.gdstash.util.GDMsgFormatter;
import org.gdstash.util.GDMsgLogger;

import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.sql.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

public class DBItem implements Comparable<DBItem>, ParameterSet {
    public static final String TABLE_NAME = "GD_ITEM";
    public static final String FIELD_ID = "ITEM_ID";
    public static final String CLASS_WEAPON_2H_SPEAR = "WeaponMelee_Spear2h";
    private static final int ROW_ITEM_ID = 1;
    private static final int ROW_ITEM_CLASS = 2;
    private static final int ROW_ARMOR_CLASS = 3;
    private static final int ROW_ARTIFACT_CLASS = 4;
    private static final int ROW_MESH_ID = 5;
    private static final int ROW_BASE_TEXTURE_ID = 6;
    private static final int ROW_BUMP_TEXTURE_ID = 7;
    private static final int ROW_GLOW_TEXTURE_ID = 8;
    private static final int ROW_SHADER_ID = 9;
    private static final int ROW_BITMAP_ID = 10;
    private static final int ROW_BITMAP = 11;
    private static final int ROW_SHARD_BITMAP_ID = 12;
    private static final int ROW_SHARD_BITMAP = 13;
    private static final int ROW_GENDER_CODE = 14;
    private static final int ROW_NAME = 15;
    private static final int ROW_DESCRIPTION = 16;
    private static final int ROW_QUALITY_TAG = 17;
    private static final int ROW_QUALITY_TEXT = 18;
    private static final int ROW_STYLE_TAG = 19;
    private static final int ROW_STYLE_TEXT = 20;
    private static final int ROW_NAME_FULL = 21;
    private static final int ROW_RARITY = 22;
    private static final int ROW_SET_ID = 23;
    private static final int ROW_SET_NAME = 24;
    private static final int ROW_BONUS_AFFIXSET_ID = 25;
    private static final int ROW_ITEM_SKILL_ID = 26;
    private static final int ROW_ITEM_SKILL_LEVEL = 27;
    private static final int ROW_PET_BONUS_SKILL_ID = 28;
    private static final int ROW_CONTROLLER_ID = 29;
    private static final int ROW_COST_FORMULASET_ID = 30;
    private static final int ROW_CONVERT_IN = 31;
    private static final int ROW_CONVERT_OUT = 32;
    private static final int ROW_CONVERT_IN_2 = 33;
    private static final int ROW_CONVERT_OUT_2 = 34;
    private static final int ROW_SOULBOUND = 35;
    private static final int ROW_HIDE_PREFIX = 36;
    private static final int ROW_HIDE_SUFFIX = 37;
    private static final int ROW_QUESTITEM = 38;
    private static final int ROW_CANNOT_PICKUP = 39;
    private static final int ROW_ENEMY_ONLY = 40;
    private static final int ROW_LEVEL = 41;
    private static final int ROW_REQ_LEVEL = 42;
    private static final int ROW_REQ_DEX = 43;
    private static final int ROW_REQ_INT = 44;
    private static final int ROW_REQ_STR = 45;
    private static final int ROW_OFFENSIVE_CHANCE = 46;
    private static final int ROW_RETALIATION_CHANCE = 47;
    private static final int ROW_PLUS_ALLSKILLS = 48;
    private static final int ROW_COMPONENT_PIECES = 49;
    private static final int ROW_MAX_STACKSIZE = 50;
    private static final int ROW_SCALE_PERCENT = 51;
    private static final int ROW_SLOT_AXE_1H = 52;
    private static final int ROW_SLOT_AXE_2H = 53;
    private static final int ROW_SLOT_DAGGER_1H = 54;
    private static final int ROW_SLOT_MACE_1H = 55;
    private static final int ROW_SLOT_MACE_2H = 56;
    private static final int ROW_SLOT_SCEPTER_1H = 57;
    private static final int ROW_SLOT_SPEAR_1H = 58;
    private static final int ROW_SLOT_SPEAR_2H = 59;
    private static final int ROW_SLOT_STAFF_2H = 60;
    private static final int ROW_SLOT_SWORD_1H = 61;
    private static final int ROW_SLOT_SWORD_2H = 62;
    private static final int ROW_SLOT_RANGED_1H = 63;
    private static final int ROW_SLOT_RANGED_2H = 64;
    private static final int ROW_SLOT_SHIELD = 65;
    private static final int ROW_SLOT_OFFHAND = 66;
    private static final int ROW_SLOT_AMULET = 67;
    private static final int ROW_SLOT_BELT = 68;
    private static final int ROW_SLOT_MEDAL = 69;
    private static final int ROW_SLOT_RING = 70;
    private static final int ROW_SLOT_HEAD = 71;
    private static final int ROW_SLOT_SHOULDERS = 72;
    private static final int ROW_SLOT_CHEST = 73;
    private static final int ROW_SLOT_HANDS = 74;
    private static final int ROW_SLOT_LEGS = 75;
    public static final int GENDERCODE_UNDEF = -1;
    public static final int GENDERCODE_MALE_SINGLE = 0;
    public static final int GENDERCODE_FEMALE_SINGLE = 1;
    public static final int GENDERCODE_NEUTRAL_SINGLE = 2;
    public static final int GENDERCODE_MALE_PLURAL = 3;
    public static final int GENDERCODE_FEMALE_PLURAL = 4;
    public static final int GENDERCODE_NEUTRAL_PLURAL = 5;
    public static final String CLASS_ARMOR_HEAD = "ArmorProtective_Head";
    public static final String CLASS_ARMOR_SHOULDERS = "ArmorProtective_Shoulders";
    public static final String CLASS_ARMOR_CHEST = "ArmorProtective_Chest";
    public static final String CLASS_ARMOR_HANDS = "ArmorProtective_Hands";
    public static final String CLASS_ARMOR_BELT = "ArmorProtective_Waist";
    public static final String CLASS_ARMOR_LEGS = "ArmorProtective_Legs";
    public static final String CLASS_ARMOR_FEET = "ArmorProtective_Feet";
    public static final String CLASS_JEWELRY_AMULET = "ArmorJewelry_Amulet";
    public static final String CLASS_JEWELRY_MEDAL = "ArmorJewelry_Medal";
    public static final String CLASS_JEWELRY_RING = "ArmorJewelry_Ring";
    public static final String CLASS_WEAPON_OFFHAND = "WeaponArmor_Offhand";
    public static final String CLASS_WEAPON_SHIELD = "WeaponArmor_Shield";
    public static final String CLASS_WEAPON_1H_AXE = "WeaponMelee_Axe";
    public static final String CLASS_WEAPON_1H_DAGGER = "WeaponMelee_Dagger";
    public static final String CLASS_WEAPON_1H_MACE = "WeaponMelee_Mace";
    public static final String CLASS_WEAPON_1H_SCEPTER = "WeaponMelee_Scepter";
    public static final String CLASS_WEAPON_1H_SPEAR = "WeaponHunting_Spear";
    public static final String CLASS_WEAPON_1H_SWORD = "WeaponMelee_Sword";
    public static final String CLASS_WEAPON_1H_RANGED = "WeaponHunting_Ranged1h";
    public static final String CLASS_WEAPON_2H_AXE = "WeaponMelee_Axe2h";
    public static final String CLASS_WEAPON_2H_MACE = "WeaponMelee_Mace2h";
    private static final int ROW_SLOT_FEET = 76;
    public static final String CLASS_WEAPON_2H_STAFF = "WeaponMagical_Staff";
    public static final String CLASS_WEAPON_2H_SWORD = "WeaponMelee_Sword2h";
    public static final String CLASS_WEAPON_2H_RANGED = "WeaponHunting_Ranged2h";
    public static final String CLASS_ARTIFACT = "ItemArtifact";
    public static final String CLASS_COMPONENT = "ItemRelic";
    public static final String CLASS_FORMULA_ARTIFACT = "ItemArtifactFormula";
    public static final String CLASS_POTION_HEALTH = "OneShot_PotionHealth";
    public static final String CLASS_POTION_MANA = "OneShot_PotionMana";
    public static final String CLASS_POTION_ENDLESS_DUNGEON = "OneShot_EndlessDungeon";
    public static final String CLASS_TONIC_CLARITY = "ItemDevotionReset";
    public static final String CLASS_TONIC_OF_RESHAPING = "ItemAttributeReset";
    public static final String CLASS_POTION_SCROLL = "OneShot_Scroll";
    public static final String CLASS_POTION_CATACLYSM = "OneShot_Potion";
    public static final String CLASS_ENCHANTMENT = "ItemEnchantment";
    public static final String CLASS_FACTION_BOOSTER = "ItemFactionBooster";
    public static final String CLASS_QUESTITEM = "QuestItem";
    public static final String CLASS_TRANSMUTER = "ItemTransmuter";
    public static final String CLASS_FACTION_WARRANT = "ItemFactionWarrant";
    public static final String CLASS_CRUCIBLE_UNLOCK = "ItemDifficultyUnlock";
    public static final String CLASS_NOTE = "ItemNote";
    public static final String CLASS_USABLE_SKILL = "ItemUsableSkill";
    public static final String CLASS_KICKSTARTER = "Kickstarter";
    public static final String RARITY_BROKEN = "Broken";
    public static final String RARITY_COMMON = "Common";
    public static final String RARITY_MAGICAL = "Magical";
    public static final String RARITY_RARE = "Rare";
    public static final String RARITY_EPIC = "Epic";
    public static final String RARITY_LEGENDARY = "Legendary";
    public static final String RARITY_QUEST = "Quest";
    public static final int RARITY_INT_BROKEN = 1;
    public static final int RARITY_INT_COMMON = 2;
    public static final int RARITY_INT_MAGICAL = 3;
    public static final int RARITY_INT_RARE = 4;
    public static final int RARITY_INT_EPIC = 5;
    public static final int RARITY_INT_LEGENDARY = 6;
    public static final int RARITY_INT_QUEST = 7;
    public static final String ARMOR_CLASS_CASTER = "Caster";
    public static final String ARMOR_CLASS_LIGHT = "Light";
    public static final String ARMOR_CLASS_HEAVY = "Heavy";
    public static final String ARTIFACT_CLASS_LESSER = "Lesser";
    public static final String ARTIFACT_CLASS_GREATER = "Greater";
    public static final String ARTIFACT_CLASS_DIVINE = "Divine";
    public static final String CONV_DAMAGE_TYPE_AETHER = "Aether";
    public static final String CONV_DAMAGE_TYPE_CHAOS = "Chaos";
    public static final String CONV_DAMAGE_TYPE_COLD = "Cold";
    public static final String CONV_DAMAGE_TYPE_ELEMENTAL = "Elemental";
    public static final String CONV_DAMAGE_TYPE_FIRE = "Fire";
    public static final String CONV_DAMAGE_TYPE_VITALITY = "Life";
    public static final String CONV_DAMAGE_TYPE_LIGHTNING = "Lightning";
    public static final String CONV_DAMAGE_TYPE_PHYSICAL = "Physical";
    public static final String CONV_DAMAGE_TYPE_PIERCE = "Pierce";
    public static final String CONV_DAMAGE_TYPE_ACID = "Poison";
    public static final String ITEM_QUEST_AETHER_CRYSTAL = "records/items/materia/compa_aethercrystal.dbr";
    public static final String ITEM_QUEST_AETHER_CLUSTER = "records/items/crafting/materials/craft_aethercrystalcluster.dbr";
    public static final String ITEM_QUEST_AETHER_SHARD = "records/items/crafting/materials/craft_aethershard.dbr";
    public static final String ITEM_QUEST_AETHERIAL_MISSIVE = "records/items/crafting/materials/craft_aetherialmissive.dbr";
    public static final String ITEM_QUEST_AETHERIAL_MUTAGEN = "records/items/crafting/materials/craft_aetherialmutagen.dbr";
    public static final String ITEM_QUEST_ANCIENT_HEART = "records/items/crafting/materials/craft_ancientheart.dbr";
    public static final String ITEM_QUEST_BLOOD_CHTHON = "records/items/crafting/materials/craft_bloodchthon.dbr";
    public static final String ITEM_QUEST_BLOOD_WATCHERS = "records/items/questitems/quest_areag_bysmielstatueessence.dbr";
    public static final String ITEM_QUEST_CELESTIAL_ESSENCE = "records/items/crafting/materials/craft_sacrifice.dbr";
    public static final String ITEM_QUEST_CELESTIAL_LOTUS = "records/items/crafting/materials/craft_celestiallotus.dbr";
    public static final String ITEM_QUEST_CHTHONIC_SEAL = "records/items/crafting/materials/craft_cultistseal.dbr";
    public static final String ITEM_QUEST_DYNAMITE = "records/items/questitems/quest_dynamite.dbr";
    public static final String ITEM_QUEST_ELDRITCH_ESSENCE = "records/items/crafting/materials/craft_eldritchessence.dbr";
    public static final String ITEM_QUEST_MANTICORE_EYE = "records/items/crafting/materials/craft_manticore.dbr";
    public static final String ITEM_QUEST_ROYAL_JELLY = "records/items/crafting/materials/craft_royaljelly.dbr";
    public static final String ITEM_QUEST_SCRAPMETAL = "records/items/questitems/scrapmetal.dbr";
    public static final String ITEM_QUEST_SKELETON_KEY = "records/items/crafting/materials/craft_skeletonkey.dbr";
    public static final String ITEM_QUEST_TAINTED_BRAIN = "records/items/crafting/materials/craft_taintedbrain.dbr";
    public static final String ITEM_QUEST_UGDENBLOOM = "records/items/crafting/materials/craft_ugdenbloom.dbr";
    public static final String ITEM_QUEST_WENDIGO_SPIRIT = "records/items/crafting/materials/craft_wendigospirit.dbr";
    public static final String ITEM_QUEST_FORGOTTEN_PASSAGE = "records/items/questitems/quest_forgottenpassage.dbr";
    public static final String ITEM_QUEST_LEGION_INSIGNIA = "records/items/questitems/quest_blacklegioninsignia.dbr.dbr";
    public static final String ITEM_QUEST_SLITH_NECKLACE = "records/items/questitems/quest_slithnecklace.dbr.dbr";
    public static final List<String> QUEST_ITEMS;
    private static ConcurrentHashMap<String, DBItem> hashBuffer;
    private String itemID;
    private String itemClass;
    private String armorClass;
    private String artifactClass;
    private String meshID;
    private String baseTextureID;
    private String bumpTextureID;
    private String glowTextureID;
    private String shaderID;
    private String bitmapID;
    private byte[] bitmap;
    private String shardBitmapID;
    private byte[] shardBitmap;
    private int genderCode;
    private String name;
    private String description;
    private String qualityTag;
    private String qualityText;
    private String styleTag;
    private String styleText;
    private String nameFull;
    private String rarity;
    private String setID;
    private String setName;
    private String bonusAffixSetID;
    private String itemSkillID;
    private int itemSkillLevel;
    private String petBonusSkillID;
    private String controllerID;
    private String costFormulaSetID;
    private String convertIn;
    private String convertOut;
    private String convertIn2;
    private String convertOut2;
    private boolean soulbound;
    private boolean hidePrefix;
    private boolean hideSuffix;
    private boolean questItem;
    private boolean cannotPickup;
    private boolean enemyOnly;
    private int itemLevel;
    private int reqLevel;
    private int reqDex;
    private int reqInt;
    private int reqStr;
    private int offensiveChance;
    private int retaliationChance;
    private int plusAllSkills;
    private int componentPieces;
    private int maxStackSize;
    private int scalePercent;
    private ItemSlots slots;
    private DBAffixSet bonusAffixSet;
    private List<DBStat> stats;
    private List<DBStatBonusRace> statBonusRaces;
    private List<DBSkillBonus> bonuses;
    private List<DBSkillModifier> skillModifiers;
    private DBSkill dbItemSkill;
    private DBSkill dbPetBonusSkill;
    private DBController dbController;
    private DBFormulaSet dbFormulaSet;
    private DBItemCraft dbCraft;
    private DBItemCraft dbBlueprint;
    private DBItemSet dbItemSet;
    private BufferedImage image;
    private BufferedImage shardImage;
    private BufferedImage overlayImage;
    private BufferedImage overlayShard;
    private boolean craftable;
    private String faction;

    public DBItem() {
        this.stats = new LinkedList<DBStat>();
        this.statBonusRaces = new LinkedList<DBStatBonusRace>();
        this.bonuses = new LinkedList<DBSkillBonus>();
        this.skillModifiers = new LinkedList<DBSkillModifier>();
        this.slots = new ItemSlots();
        this.dbCraft = new DBItemCraft();
    }

    private DBItem(final ARZRecord record) {
        this();
        this.itemID = record.getFileName();
        this.enemyOnly = (this.itemID.startsWith("records/items/enemygear/") || this.itemID.startsWith("records/creatures/"));
        final String dlc = record.getDLCRequirement();
        this.setItemClass(record.getRecordClass(), record.getFileName(), dlc);
        this.setQuestItem(record.isQuestItem());
        this.genderCode = record.getGenderCode();
        this.name = record.getItemName();
        if (this.name == null) {
            this.name = record.getFileDescription();
        }
        this.description = record.getItemDescription();
        this.setQualityTag(record.getQualityTag());
        this.setStyleTag(record.getStyleTag());
        this.nameFull = this.getItemName();
        this.setCraftID(record.getArtifactID());
        this.armorClass = record.getArmorClass();
        this.artifactClass = record.getArtifactClass();
        this.meshID = record.getMeshID();
        this.baseTextureID = record.getBaseTextureID();
        this.bumpTextureID = record.getBumpTextureID();
        this.glowTextureID = record.getGlowTextureID();
        this.shaderID = record.getShaderID();
        this.bitmapID = record.getBitmapID();
        this.shardBitmapID = record.getShardBitmapID();
        this.rarity = record.getRarity();
        this.setID = record.getItemSetID();
        this.setItemSetNameTag(record.getItemSetNameTag());
        this.bonusAffixSetID = record.getBonusAffixSetID();
        this.itemSkillID = record.getItemSkillID();
        this.itemSkillLevel = record.getItemSkillLevel();
        this.petBonusSkillID = record.getPetBonusID();
        this.controllerID = record.getSkillControllerID();
        this.convertIn = record.getConversionIn();
        this.convertOut = record.getConversionOut();
        this.convertIn2 = record.getConversionIn2();
        this.convertOut2 = record.getConversionOut2();
        this.soulbound = record.isSoulbound();
        this.hidePrefix = record.isHidePrefix();
        this.hideSuffix = record.isHideSuffix();
        this.cannotPickup = record.isCannotPickup();
        if (this.cannotPickup) {
            this.enemyOnly = true;
        }
        this.itemLevel = record.getItemLevel();
        this.reqLevel = record.getRequiredLevel();
        this.offensiveChance = record.getOffensiveChance();
        this.retaliationChance = record.getRetaliationChance();
        this.plusAllSkills = record.getPlusAllSkills();
        this.componentPieces = record.getComponentPieces();
        this.maxStackSize = record.getMaxStackSize();
        this.scalePercent = record.getRNGPercent();
        this.slots = record.getItemSlots();
        this.stats = record.getDBStatList();
        this.statBonusRaces = record.getDBStatBonusRaceList();
        this.bonuses = record.dbSkillBonuses;
        this.skillModifiers = record.getSkillModifierList();
        this.setCostFormulaSetID(record.getCostFormulaSetID());
    }

    @Override
    public boolean equals(final Object o) {
        if (o == null) {
            return false;
        }
        if (!o.getClass().equals(DBItem.class)) {
            return false;
        }
        final DBItem item = (DBItem) o;
        return item.itemID.equals(this.itemID);
    }

    @Override
    public int hashCode() {
        return this.itemID.hashCode();
    }

    @Override
    public int compareTo(final DBItem o) {
        if (!o.getClass().equals(DBItem.class)) {
            final Object[] args = {DBItem.class, o.toString()};
            final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_NOT_OF_TYPE", args);
            throw new ClassCastException(msg);
        }
        final DBItem item = o;
        final int oClass = ItemClass.getClassInt(this.itemClass);
        final int iClass = ItemClass.getClassInt(item.itemClass);
        if (oClass < iClass) {
            return -1;
        }
        if (oClass > iClass) {
            return 1;
        }
        final int oRarity = this.getRarityInt();
        final int iRarity = item.getRarityInt();
        if (oRarity < iRarity) {
            return -1;
        }
        if (oRarity > iRarity) {
            return 1;
        }
        final String fullname1 = this.combineName(true);
        final String fullname2 = item.combineName(true);
        if (fullname1 == null) {
            if (fullname2 != null) {
                return -1;
            }
        } else {
            if (fullname2 == null) {
                return 1;
            }
            if (!fullname2.equals(fullname1)) {
                return fullname1.compareTo(fullname2);
            }
        }
        if (this.reqLevel < item.reqLevel) {
            return -1;
        }
        if (this.reqLevel > item.reqLevel) {
            return 1;
        }
        return 0;
    }

    public String getItemID() {
        return this.itemID;
    }

    @Override
    public String getItemClass() {
        return this.itemClass;
    }

    public String getArmorClass() {
        return this.armorClass;
    }

    public String getArtifactClass() {
        return this.artifactClass;
    }

    public int getGenderCode() {
        return this.genderCode;
    }

    public String getItemDescription() {
        return GDColor.replaceTags(this.description);
    }

    public String getName(final boolean inclFaction) {
        if (inclFaction && this.faction != null) {
            return this.name + " (" + this.faction + ")";
        }
        return this.name;
    }

    public String getFullName() {
        return this.nameFull;
    }

    public String getFaction() {
        return this.faction;
    }

    public String getItemName() {
        return this.combineName(false);
    }

    public String getItemNameWithDmg() {
        return this.combineName(true);
    }

    public int getItemScalePercent() {
        return this.scalePercent;
    }

    public String getQualityText() {
        return this.qualityText;
    }

    public String getStyleText() {
        return this.styleText;
    }

    public String getRarity() {
        return this.rarity;
    }

    public String getItemSetID() {
        return this.setID;
    }

    public String getItemSetName() {
        return this.setName;
    }

    public ArrayList<List<DBStat>> getItemSetBonusesPerLevel() {
        if (this.dbItemSet == null) {
            return null;
        }
        return this.dbItemSet.getBonusesPerLevel();
    }

    public ArrayList<List<DBSkillBonus>> getItemSetSkillBonusesPerLevel() {
        if (this.dbItemSet == null) {
            return null;
        }
        return this.dbItemSet.getItemSetSkillBonusesPerLevel();
    }

    public List<DBSkillModifier> getSkillModifiers(final int level) {
        if (this.dbItemSet == null) {
            return null;
        }
        return this.dbItemSet.getSkillModifiers(level);
    }

    public int getConvertPerc() {
        final DBStat stat = DBStat.getDBStat(this.stats, "conversionPercentage", 1);
        if (stat == null) {
            return 0;
        }
        return (int) stat.getStatMin();
    }

    public int getConvertPerc2() {
        final DBStat stat = DBStat.getDBStat(this.stats, "conversionPercentage2", 1);
        if (stat == null) {
            return 0;
        }
        return (int) stat.getStatMin();
    }

    public String getCraftID() {
        return this.dbCraft.getCraftID();
    }

    public String getBonusAffixSetID() {
        return this.bonusAffixSetID;
    }

    static {
        (QUEST_ITEMS = new LinkedList<String>()).add("records/items/materia/compa_aethercrystal.dbr");
        DBItem.QUEST_ITEMS.add("records/items/crafting/materials/craft_aethercrystalcluster.dbr");
        DBItem.QUEST_ITEMS.add("records/items/crafting/materials/craft_aethershard.dbr");
        DBItem.QUEST_ITEMS.add("records/items/crafting/materials/craft_aetherialmissive.dbr");
        DBItem.QUEST_ITEMS.add("records/items/crafting/materials/craft_aetherialmutagen.dbr");
        DBItem.QUEST_ITEMS.add("records/items/crafting/materials/craft_ancientheart.dbr");
        DBItem.QUEST_ITEMS.add("records/items/crafting/materials/craft_bloodchthon.dbr");
        DBItem.QUEST_ITEMS.add("records/items/questitems/quest_areag_bysmielstatueessence.dbr");
        DBItem.QUEST_ITEMS.add("records/items/crafting/materials/craft_sacrifice.dbr");
        DBItem.QUEST_ITEMS.add("records/items/crafting/materials/craft_celestiallotus.dbr");
        DBItem.QUEST_ITEMS.add("records/items/crafting/materials/craft_cultistseal.dbr");
        DBItem.QUEST_ITEMS.add("records/items/questitems/quest_dynamite.dbr");
        DBItem.QUEST_ITEMS.add("records/items/crafting/materials/craft_eldritchessence.dbr");
        DBItem.QUEST_ITEMS.add("records/items/crafting/materials/craft_manticore.dbr");
        DBItem.QUEST_ITEMS.add("records/items/crafting/materials/craft_royaljelly.dbr");
        DBItem.QUEST_ITEMS.add("records/items/questitems/scrapmetal.dbr");
        DBItem.QUEST_ITEMS.add("records/items/crafting/materials/craft_skeletonkey.dbr");
        DBItem.QUEST_ITEMS.add("records/items/crafting/materials/craft_taintedbrain.dbr");
        DBItem.QUEST_ITEMS.add("records/items/crafting/materials/craft_ugdenbloom.dbr");
        DBItem.QUEST_ITEMS.add("records/items/crafting/materials/craft_wendigospirit.dbr");
        DBItem.QUEST_ITEMS.add("records/items/questitems/quest_blacklegioninsignia.dbr.dbr");
        DBItem.QUEST_ITEMS.add("records/items/questitems/quest_slithnecklace.dbr.dbr");
        DBItem.hashBuffer = new ConcurrentHashMap<String, DBItem>();
    }

    public String getItemSkillID() {
        return this.itemSkillID;
    }

    public int getItemSkillLevel() {
        return this.itemSkillLevel;
    }

    public String getPetBonusSkillID() {
        return this.petBonusSkillID;
    }

    public String getControllerID() {
        return this.controllerID;
    }

    public String getControllerTriggerType() {
        if (this.dbController == null) {
            return null;
        }
        return this.dbController.getTriggerType();
    }

    public int getControllerTriggerChance() {
        if (this.dbController == null) {
            return 0;
        }
        return this.dbController.getTriggerChance();
    }

    public String getCostFormulaSetID() {
        return this.costFormulaSetID;
    }

    public boolean isSoulbound() {
        return this.soulbound;
    }

    public boolean isHidePrefix() {
        return this.hidePrefix;
    }

    public boolean isHideSuffix() {
        return this.hideSuffix;
    }

    public boolean isQuestItem() {
        return this.questItem;
    }

    public boolean isCraftable() {
        return this.craftable;
    }

    @Override
    public int getItemLevel() {
        return this.itemLevel;
    }

    public boolean isEnemyOnly() {
        return this.enemyOnly;
    }

    public int getRequiredlevel() {
        return this.reqLevel;
    }

    public int getRequiredCunning() {
        return this.reqDex;
    }

    public int getRequiredPhysique() {
        return this.reqStr;
    }

    public int getRequiredSpirit() {
        return this.reqInt;
    }

    public int getOffensiveChance() {
        return this.offensiveChance;
    }

    public int getRetaliationChance() {
        return this.retaliationChance;
    }

    public String getConvertIn() {
        return this.convertIn;
    }

    public String getConvertOut() {
        return this.convertOut;
    }

    public String getConvertIn2() {
        return this.convertIn2;
    }

    public String getConvertOut2() {
        return this.convertOut2;
    }

    public int getPlusAllSkills() {
        return this.plusAllSkills;
    }

    public int getComponentPieces() {
        return this.componentPieces;
    }

    public int getMaxStackSize() {
        return this.maxStackSize;
    }

    public ItemSlots getSlots() {
        return this.slots.clone();
    }

    public boolean usesSlots() {
        return this.slots != null && this.slots.usesSlots();
    }

    public boolean isSlotAxe1H() {
        return this.slots.slotAxe1H;
    }

    public boolean isSlotAxe2H() {
        return this.slots.slotAxe2H;
    }

    public boolean isSlotDagger1H() {
        return this.slots.slotDagger1H;
    }

    public boolean isSlotMace1H() {
        return this.slots.slotMace1H;
    }

    public boolean isSlotMace2H() {
        return this.slots.slotMace2H;
    }

    public boolean isSlotScepter1H() {
        return this.slots.slotScepter1H;
    }

    public boolean isSlotSpear1H() {
        return this.slots.slotSpear1H;
    }

    public static void clearBuffer() {
        DBItem.hashBuffer.clear();
    }

    public boolean isSlotStaff2H() {
        return this.slots.slotStaff2H;
    }

    public boolean isSlotSword1H() {
        return this.slots.slotSword1H;
    }

    public boolean isSlotSword2H() {
        return this.slots.slotSword2H;
    }

    public boolean isSlotRanged1H() {
        return this.slots.slotRanged1H;
    }

    public boolean isSlotRanged2H() {
        return this.slots.slotRanged2H;
    }

    public boolean isSlotShield() {
        return this.slots.slotShield;
    }

    public boolean isSlotOffhand() {
        return this.slots.slotOffhand;
    }

    public boolean isSlotAmulet() {
        return this.slots.slotAmulet;
    }

    public boolean isSlotBelt() {
        return this.slots.slotBelt;
    }

    public boolean isSlotMedal() {
        return this.slots.slotMedal;
    }

    public boolean isSlotRing() {
        return this.slots.slotRing;
    }

    public boolean isSlotHead() {
        return this.slots.slotHead;
    }

    public boolean isSlotShoulders() {
        return this.slots.slotShoulders;
    }

    public boolean isSlotChest() {
        return this.slots.slotChest;
    }

    public boolean isSlotHands() {
        return this.slots.slotHands;
    }

    public boolean isSlotLegs() {
        return this.slots.slotLegs;
    }

    public boolean isSlotFeet() {
        return this.slots.slotFeet;
    }

    public DBAffixSet getBonusAffixSet() {
        return this.bonusAffixSet;
    }

    public List<DBStat> getStatList() {
        return this.stats;
    }

    public List<DBSkillBonus> getSkillBonusList() {
        return this.bonuses;
    }

    public List<DBSkillModifier> getSkillModifierList() {
        return this.skillModifiers;
    }

    public DBSkill getItemSkill() {
        return this.dbItemSkill;
    }

    public DBController getItemSkillController() {
        return this.dbController;
    }

    public DBSkill getPetBonusSkill() {
        return this.dbPetBonusSkill;
    }

    public byte[] getBitmap() {
        return this.bitmap;
    }

    public BufferedImage getImage() {
        return this.image;
    }

    public String getMeshID() {
        return this.meshID;
    }

    public String getBaseTextureID() {
        return this.baseTextureID;
    }

    public String getBumpTextureID() {
        return this.bumpTextureID;
    }

    public String getGlowTextureID() {
        return this.glowTextureID;
    }

    public String getShaderID() {
        return this.shaderID;
    }

    public String getImageID() {
        return this.bitmapID;
    }

    public BufferedImage getShardImage() {
        return this.shardImage;
    }

    public BufferedImage getOverlayImage() {
        return this.overlayImage;
    }

    public BufferedImage getOverlayShard() {
        return this.overlayShard;
    }

    public DBFormulaSet.Result getStatRequirements(final ParameterSet ps) {
        if (this.dbFormulaSet == null) {
            return null;
        }
        final DBFormulaSet.Result result = this.dbFormulaSet.getResult(ps);
        if (result != null) {
            result.cunning = (int) (result.cunning + 0.5);
            result.physique = (int) (result.physique + 0.5);
            result.spirit = (int) (result.spirit + 0.5);
        }
        return result;
    }

    public void determineStatRequirements() {
        final DBFormulaSet.Result result = this.getStatRequirements(this);
        if (result != null) {
            this.reqDex = (int) result.cunning;
            this.reqStr = (int) result.physique;
            this.reqInt = (int) result.spirit;
        }
    }

    @Override
    public float getCharAttackSpeed() {
        final DBStat stat = DBStat.getDBStat(this.stats, "characterAttackSpeed", 1);
        if (stat == null) {
            return 0.0f;
        }
        return stat.getStatMin();
    }

    @Override
    public int getDefenseAttrArmor() {
        final DBStat stat = DBStat.getDBStat(this.stats, "defensiveProtection", 1);
        if (stat == null) {
            return 0;
        }
        return (int) stat.getStatMin();
    }

    @Override
    public int getShieldBlockDefense() {
        final DBStat stat = DBStat.getDBStat(this.stats, "defensiveBlock", 1);
        if (stat == null) {
            return 0;
        }
        return (int) stat.getStatMin();
    }

    @Override
    public int getShieldBlockChance() {
        final DBStat stat = DBStat.getDBStat(this.stats, "defensiveBlock", 1);
        if (stat == null) {
            return 0;
        }
        return stat.getStatChance();
    }

    @Override
    public int getDamageAvgPierceRatio() {
        final DBStat stat = DBStat.getDBStat(this.stats, "offensivePierceRatio", 1);
        if (stat == null) {
            return 0;
        }
        int minVal = (int) stat.getStatMin();
        final int maxVal = (int) stat.getStatMax();
        if (maxVal > 0) {
            minVal += maxVal;
            minVal /= 2;
        }
        return minVal;
    }

    @Override
    public int getDamageAvgBase() {
        int total = 0;
        int minVal = 0;
        int maxVal = 0;
        for (final DBStat stat : this.stats) {
            if (stat.getStatType().equals("offensiveBaseAether")) {
                minVal = (int) stat.getStatMin();
                maxVal = (int) stat.getStatMax();
                if (maxVal > 0) {
                    minVal += maxVal;
                    minVal /= 2;
                }
                total += minVal;
            }
            if (stat.getStatType().equals("offensiveBaseChaos")) {
                minVal = (int) stat.getStatMin();
                maxVal = (int) stat.getStatMax();
                if (maxVal > 0) {
                    minVal += maxVal;
                    minVal /= 2;
                }
                total += minVal;
            }
            if (stat.getStatType().equals("offensiveBaseCold")) {
                minVal = (int) stat.getStatMin();
                maxVal = (int) stat.getStatMax();
                if (maxVal > 0) {
                    minVal += maxVal;
                    minVal /= 2;
                }
                total += minVal;
            }
            if (stat.getStatType().equals("offensiveBaseFire")) {
                minVal = (int) stat.getStatMin();
                maxVal = (int) stat.getStatMax();
                if (maxVal > 0) {
                    minVal += maxVal;
                    minVal /= 2;
                }
                total += minVal;
            }
            if (stat.getStatType().equals("offensiveBaseLife")) {
                minVal = (int) stat.getStatMin();
                maxVal = (int) stat.getStatMax();
                if (maxVal > 0) {
                    minVal += maxVal;
                    minVal /= 2;
                }
                total += minVal;
            }
            if (stat.getStatType().equals("offensiveBaseLightning")) {
                minVal = (int) stat.getStatMin();
                maxVal = (int) stat.getStatMax();
                if (maxVal > 0) {
                    minVal += maxVal;
                    minVal /= 2;
                }
                total += minVal;
            }
            if (stat.getStatType().equals("offensivePhysical")) {
                minVal = (int) stat.getStatMin();
                maxVal = (int) stat.getStatMax();
                if (maxVal > 0) {
                    minVal += maxVal;
                    minVal /= 2;
                }
                total += minVal;
            }
            if (stat.getStatType().equals("offensiveBasePoison")) {
                minVal = (int) stat.getStatMin();
                maxVal = (int) stat.getStatMax();
                if (maxVal > 0) {
                    minVal += maxVal;
                    minVal /= 2;
                }
                total += minVal;
            }
        }
        return total;
    }

    @Override
    public int getTotalAttCount() {
        int total = 0;
        for (final DBStat stat : this.stats) {
            if (stat.getStatMin() > 0.0f) {
                ++total;
            }
            if (stat.getStatModifier() > 0) {
                ++total;
            }
            if (stat.getDurationModifier() > 0) {
                ++total;
            }
            if (stat.getMaxResist() > 0) {
                ++total;
            }
        }
        if (this.statBonusRaces != null && !this.statBonusRaces.isEmpty()) {
            total += this.statBonusRaces.size();
        }
        return total;
    }

    @Override
    public int getItemPrefixCost() {
        return 0;
    }

    @Override
    public int getItemSuffixCost() {
        return 0;
    }

    public static void createTables() throws SQLException {
        final String dropTable = "DROP TABLE GD_ITEM";
        final String createTable = "CREATE TABLE GD_ITEM (ITEM_ID       VARCHAR(256) NOT NULL, ITEM_CLASS         VARCHAR(32), ARMOR_CLASS        VARCHAR(32), ARTIFACT_CLASS     VARCHAR(32), MESH_ID            VARCHAR(256), BASE_TEXTURE_ID    VARCHAR(512), BUMP_TEXTURE_ID    VARCHAR(512), GLOW_TEXTURE_ID    VARCHAR(512), SHADER_ID          VARCHAR(256), BITMAP_ID          VARCHAR(256), BITMAP             BLOB(128K), SHARD_BITMAP_ID    VARCHAR(256), SHARD_BITMAP       BLOB(32K), GENDER_CODE        INTEGER, NAME               VARCHAR(256), DESCRIPTION        VARCHAR(16000), QUALITY_TAG        VARCHAR(64), QUALITY_TEXT       VARCHAR(64), STYLE_TAG          VARCHAR(64), STYLE_TEXT         VARCHAR(64), NAME_FULL          VARCHAR(256), RARITY             VARCHAR(32), SET_ID             VARCHAR(256), SET_NAME           VARCHAR(256), BONUS_AFFIXSET_ID  VARCHAR(256), ITEM_SKILL_ID      VARCHAR(256), ITEM_SKILL_LEVEL   INTEGER,PET_BONUS_SKILL_ID VARCHAR(256), CONTROLLER_ID      VARCHAR(256), COST_FORMULASET_ID VARCHAR(256), CONVERT_IN         VARCHAR(16), CONVERT_OUT        VARCHAR(16), CONVERT_IN_2       VARCHAR(16), CONVERT_OUT_2      VARCHAR(16), SOULBOUND          BOOLEAN, HIDE_PREFIX        BOOLEAN, HIDE_SUFFIX        BOOLEAN, QUESTITEM          BOOLEAN, CANNOT_PICKUP      BOOLEAN, ENEMY_ONLY         BOOLEAN, LEVEL              INTEGER, REQ_LEVEL          INTEGER, REQ_DEX            INTEGER, REQ_INT            INTEGER, REQ_STR            INTEGER, OFFENSE_PRC        INTEGER, RETAL_PRC          INTEGER, PLUS_ALLSKILLS     INTEGER, COMPONENT_PIECES   INTEGER, MAX_STACKSIZE      INTEGER, RNG_PERCENT        INTEGER, SLOT_AXE_1H        BOOLEAN, SLOT_AXE_2H        BOOLEAN, SLOT_DAGGER_1H     BOOLEAN, SLOT_MACE_1H       BOOLEAN, SLOT_MACE_2H       BOOLEAN, SLOT_SCEPTER_1H    BOOLEAN, SLOT_SPEAR_1H      BOOLEAN, SLOT_SPEAR_2H      BOOLEAN, SLOT_STAFF_2H      BOOLEAN, SLOT_SWORD_1H      BOOLEAN, SLOT_SWORD_2H      BOOLEAN, SLOT_RANGED_1H     BOOLEAN, SLOT_RANGED_2H     BOOLEAN, SLOT_SHIELD        BOOLEAN, SLOT_OFFHAND       BOOLEAN, SLOT_AMULET        BOOLEAN, SLOT_BELT          BOOLEAN, SLOT_MEDAL         BOOLEAN, SLOT_RING          BOOLEAN, SLOT_HEAD          BOOLEAN, SLOT_SHOULDERS     BOOLEAN, SLOT_CHEST         BOOLEAN, SLOT_HANDS         BOOLEAN, SLOT_LEGS          BOOLEAN, SLOT_FEET          BOOLEAN, PRIMARY KEY (ITEM_ID))";
        try (final Connection conn = GDDBData.getConnection()) {
            final boolean auto = conn.getAutoCommit();
            conn.setAutoCommit(false);
            try (final Statement st = conn.createStatement()) {
                if (GDDBUtil.tableExists(conn, "GD_ITEM")) {
                    st.execute(dropTable);
                }
                if (GDDBUtil.tableExists(conn, "GD_ITEM_CHAR")) {
                    st.execute("DROP TABLE GD_ITEM_CHAR");
                }
                if (GDDBUtil.tableExists(conn, "GD_ITEM_CHARRACES")) {
                    st.execute("DROP TABLE GD_ITEM_CHARRACES");
                }
                if (GDDBUtil.tableExists(conn, "GD_ITEM_DAMAGE")) {
                    st.execute("DROP TABLE GD_ITEM_DAMAGE");
                }
                st.execute(createTable);
                st.close();
                conn.commit();
                DBItemCraft.createTables(conn);
                DBStat.createItemTable(conn);
                DBStatBonusRace.createItemTable(conn);
                DBSkillBonus.createItemTable(conn);
                DBSkillModifier.createItemTable(conn);
            } catch (SQLException ex) {
                conn.rollback();
                final Object[] args = {"GD_ITEM"};
                final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_CREATE_TABLE", args);
                GDMsgLogger.addError(msg);
                throw ex;
            } finally {
                conn.setAutoCommit(auto);
            }
        }
    }

    public static void delete(final String itemID) throws SQLException {
        final String deleteEntry = "DELETE FROM GD_ITEM WHERE ITEM_ID = ?";
        try (final Connection conn = GDDBData.getConnection()) {
            final boolean auto = conn.getAutoCommit();
            conn.setAutoCommit(false);
            try (final PreparedStatement ps = conn.prepareStatement(deleteEntry)) {
                ps.setString(1, itemID);
                ps.executeUpdate();
                ps.close();
                DBItemCraft.delete(conn, itemID);
                DBStat.deleteItem(conn, itemID);
                DBStatBonusRace.deleteItem(conn, itemID);
                DBSkillBonus.deleteItem(conn, itemID);
                DBSkillModifier.deleteItem(conn, itemID);
                conn.commit();
            } catch (SQLException ex) {
                conn.rollback();
                final Object[] args = {itemID, "GD_ITEM"};
                final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_DEL_TABLE_BY_ID", args);
                GDMsgLogger.addError(msg);
                GDMsgLogger.addError(ex);
                throw ex;
            } finally {
                conn.setAutoCommit(auto);
            }
        } catch (SQLException ex2) {
            throw ex2;
        }
    }

    public static void insert(final ARZRecord record) throws SQLException {
        final DBItem entry = get(record.getFileName());
        if (entry != null) {
            return;
        }
        final DBItem item = new DBItem(record);
        final String insertItem = "INSERT INTO GD_ITEM VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
        try (final Connection conn = GDDBData.getConnection()) {
            final boolean auto = conn.getAutoCommit();
            conn.setAutoCommit(false);
            try (final PreparedStatement ps = conn.prepareStatement(insertItem)) {
                ps.setString(1, item.itemID);
                ps.setString(2, item.itemClass);
                ps.setString(3, item.armorClass);
                ps.setString(4, item.artifactClass);
                ps.setString(5, item.meshID);
                ps.setString(6, item.baseTextureID);
                ps.setString(7, item.bumpTextureID);
                ps.setString(8, item.glowTextureID);
                ps.setString(9, item.shaderID);
                ps.setString(10, item.bitmapID);
                if (item.bitmap == null) {
                    ps.setBlob(11, (Blob) null);
                } else {
                    ps.setBlob(11, new ByteArrayInputStream(item.bitmap));
                }
                ps.setString(12, item.shardBitmapID);
                if (item.shardBitmap == null) {
                    ps.setBlob(13, (Blob) null);
                } else {
                    ps.setBlob(13, new ByteArrayInputStream(item.shardBitmap));
                }
                ps.setInt(14, item.genderCode);
                ps.setString(15, item.name);
                ps.setString(16, item.description);
                ps.setString(17, item.qualityTag);
                ps.setString(18, item.qualityText);
                ps.setString(19, item.styleTag);
                ps.setString(20, item.styleText);
                ps.setString(21, item.nameFull);
                ps.setString(22, item.rarity);
                ps.setString(23, item.setID);
                ps.setString(24, item.setName);
                ps.setString(25, item.bonusAffixSetID);
                ps.setString(26, item.itemSkillID);
                ps.setInt(27, item.itemSkillLevel);
                ps.setString(28, item.petBonusSkillID);
                ps.setString(29, item.controllerID);
                ps.setString(30, item.costFormulaSetID);
                ps.setString(31, item.convertIn);
                ps.setString(32, item.convertOut);
                ps.setString(33, item.convertIn2);
                ps.setString(34, item.convertOut2);
                ps.setBoolean(35, item.soulbound);
                ps.setBoolean(36, item.hidePrefix);
                ps.setBoolean(37, item.hideSuffix);
                ps.setBoolean(38, item.questItem);
                ps.setBoolean(39, item.cannotPickup);
                ps.setBoolean(40, item.enemyOnly);
                ps.setInt(41, item.itemLevel);
                ps.setInt(42, item.reqLevel);
                ps.setInt(43, item.reqDex);
                ps.setInt(44, item.reqInt);
                ps.setInt(45, item.reqStr);
                ps.setInt(46, item.offensiveChance);
                ps.setInt(47, item.retaliationChance);
                ps.setInt(48, item.plusAllSkills);
                ps.setInt(49, item.componentPieces);
                ps.setInt(50, item.maxStackSize);
                ps.setInt(51, item.scalePercent);
                ps.setBoolean(52, item.slots.slotAxe1H);
                ps.setBoolean(53, item.slots.slotAxe2H);
                ps.setBoolean(54, item.slots.slotDagger1H);
                ps.setBoolean(55, item.slots.slotMace1H);
                ps.setBoolean(56, item.slots.slotMace2H);
                ps.setBoolean(57, item.slots.slotScepter1H);
                ps.setBoolean(58, item.slots.slotSpear1H);
                ps.setBoolean(59, item.slots.slotSpear2H);
                ps.setBoolean(60, item.slots.slotStaff2H);
                ps.setBoolean(61, item.slots.slotSword1H);
                ps.setBoolean(62, item.slots.slotSword2H);
                ps.setBoolean(63, item.slots.slotRanged1H);
                ps.setBoolean(64, item.slots.slotRanged2H);
                ps.setBoolean(65, item.slots.slotShield);
                ps.setBoolean(66, item.slots.slotOffhand);
                ps.setBoolean(67, item.slots.slotAmulet);
                ps.setBoolean(68, item.slots.slotBelt);
                ps.setBoolean(69, item.slots.slotMedal);
                ps.setBoolean(70, item.slots.slotRing);
                ps.setBoolean(71, item.slots.slotHead);
                ps.setBoolean(72, item.slots.slotShoulders);
                ps.setBoolean(73, item.slots.slotChest);
                ps.setBoolean(74, item.slots.slotHands);
                ps.setBoolean(75, item.slots.slotLegs);
                ps.setBoolean(76, item.slots.slotFeet);
                ps.executeUpdate();
                ps.close();
                if (item.dbCraft.getCraftID() != null) {
                    DBItemCraft.insert(conn, item);
                }
                DBStat.insertItem(conn, item.itemID, item.stats);
                DBStatBonusRace.insertItem(conn, item.itemID, item.statBonusRaces);
                DBSkillBonus.insertItem(conn, item.itemID, item.bonuses);
                DBSkillModifier.insertItem(conn, item.itemID, item.skillModifiers);
                conn.commit();
            } catch (SQLException ex) {
                conn.rollback();
                final Object[] args = {record.getFileName(), "GD_ITEM"};
                final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_INS_TABLE_BY_ID", args);
                GDMsgLogger.addLowError(msg);
                GDMsgLogger.addLowError(ex);
            } finally {
                conn.setAutoCommit(auto);
            }
        } catch (SQLException ex2) {
            throw ex2;
        }
    }

    private int getRarityInt() {
        if (this.rarity == null) {
            return -1;
        }
        if (this.rarity.equals("Broken")) {
            return 1;
        }
        if (this.rarity.equals("Common")) {
            return 2;
        }
        if (this.rarity.equals("Magical")) {
            return 3;
        }
        if (this.rarity.equals("Rare")) {
            return 4;
        }
        if (this.rarity.equals("Epic")) {
            return 5;
        }
        if (this.rarity.equals("Legendary")) {
            return 6;
        }
        if (this.rarity.equals("Quest")) {
            return 7;
        }
        if (this.rarity.equals("Lesser")) {
            return 8;
        }
        if (this.rarity.equals("Greater")) {
            return 9;
        }
        if (this.rarity.equals("Divine")) {
            return 10;
        }
        return -1;
    }

    private int getArmorClassInt() {
        if (this.armorClass == null) {
            return -1;
        }
        if (this.armorClass.equals("Caster")) {
            return 1;
        }
        if (this.armorClass.equals("Light")) {
            return 2;
        }
        if (this.armorClass.equals("Heavy")) {
            return 3;
        }
        return -1;
    }

    private int getArtifactClassInt() {
        if (this.artifactClass == null) {
            return -1;
        }
        if (this.artifactClass.equals("Lesser")) {
            return 1;
        }
        if (this.artifactClass.equals("Greater")) {
            return 2;
        }
        if (this.artifactClass.equals("Divine")) {
            return 3;
        }
        return -1;
    }

    public boolean isArmor() {
        return ItemClass.isArmor(this.itemClass);
    }

    public boolean isArtifact() {
        return ItemClass.isArtifact(this.itemClass);
    }

    public boolean isComponent() {
        return ItemClass.isComponent(this.itemClass);
    }

    public boolean isEnchantment() {
        return ItemClass.isEnchantment(this.itemClass);
    }

    public boolean isJewelry() {
        return ItemClass.isJewelry(this.itemClass);
    }

    public boolean isNote() {
        return ItemClass.isNote(this.itemClass);
    }

    public boolean isFactionBooster() {
        return ItemClass.isFactionBooster(this.itemClass);
    }

    public boolean isElixir() {
        return ItemClass.isElixir(this.itemClass);
    }

    public boolean isPotion() {
        return ItemClass.isPotion(this.itemClass);
    }

    public boolean isOffhand() {
        return ItemClass.isOffhand(this.itemClass);
    }

    public boolean isWeapon() {
        return ItemClass.isWeapon(this.itemClass);
    }

    public boolean is1HWeapon() {
        return ItemClass.is1HWeapon(this.itemClass);
    }

    public boolean is2HWeapon() {
        return ItemClass.is2HWeapon(this.itemClass);
    }

    public boolean isStackable() {
        return ItemClass.isStackable(this.itemClass);
    }

    public boolean isEpic() {
        return this.rarity != null && this.rarity.equals("Epic");
    }

    public boolean isLegendary() {
        return this.rarity != null && this.rarity.equals("Legendary");
    }

    public boolean isUnique() {
        return this.isEpic() || this.isLegendary();
    }

    public String getMainDamageType() {
        if (!this.isWeapon()) {
            return null;
        }
        DBStat maxDmgStat = null;
        int max = 0;
        int minDmg = 0;
        int maxDmg = 0;
        for (final DBStat stat : this.stats) {
            boolean found = false;
            if (stat.getStatType().equals("offensiveAether")) {
                found = true;
            }
            if (stat.getStatType().equals("offensiveBaseAether")) {
                found = true;
            }
            if (stat.getStatType().equals("offensiveChaos")) {
                found = true;
            }
            if (stat.getStatType().equals("offensiveBaseChaos")) {
                found = true;
            }
            if (stat.getStatType().equals("offensiveCold")) {
                found = true;
            }
            if (stat.getStatType().equals("offensiveBaseCold")) {
                found = true;
            }
            if (stat.getStatType().equals("offensiveElemental")) {
                found = true;
            }
            if (stat.getStatType().equals("offensiveFire")) {
                found = true;
            }
            if (stat.getStatType().equals("offensiveBaseFire")) {
                found = true;
            }
            if (stat.getStatType().equals("offensiveLife")) {
                found = true;
            }
            if (stat.getStatType().equals("offensiveBaseLife")) {
                found = true;
            }
            if (stat.getStatType().equals("offensiveLightning")) {
                found = true;
            }
            if (stat.getStatType().equals("offensiveBaseLightning")) {
                found = true;
            }
            if (stat.getStatType().equals("offensivePhysical")) {
                found = true;
            }
            if (stat.getStatType().equals("offensiveBonusPhysical")) {
                found = true;
            }
            if (stat.getStatType().equals("offensivePierce")) {
                found = true;
            }
            if (stat.getStatType().equals("offensivePoison")) {
                found = true;
            }
            if (stat.getStatType().equals("offensiveBasePoison")) {
                found = true;
            }
            if (!found) {
                continue;
            }
            if (stat.getStatChance() != 0 || stat.getStatMin() <= 0.0f) {
                continue;
            }
            minDmg = (int) stat.getStatMin();
            maxDmg = (int) stat.getStatMax();
            if (maxDmg > 0) {
                minDmg += maxDmg;
            } else {
                minDmg += minDmg;
            }
            if (minDmg <= max) {
                continue;
            }
            max = minDmg;
            maxDmgStat = stat;
        }
        String sDmg = null;
        if (maxDmgStat != null) {
            if (maxDmgStat.getStatType().equals("offensiveAether")) {
                sDmg = GDMsgFormatter.getString(GDMsgFormatter.rbGD, "OFF_AETHER");
            }
            if (maxDmgStat.getStatType().equals("offensiveBaseAether")) {
                sDmg = GDMsgFormatter.getString(GDMsgFormatter.rbGD, "OFF_AETHER");
            }
            if (maxDmgStat.getStatType().equals("offensiveChaos")) {
                sDmg = GDMsgFormatter.getString(GDMsgFormatter.rbGD, "OFF_CHAOS");
            }
            if (maxDmgStat.getStatType().equals("offensiveBaseChaos")) {
                sDmg = GDMsgFormatter.getString(GDMsgFormatter.rbGD, "OFF_CHAOS");
            }
            if (maxDmgStat.getStatType().equals("offensiveCold")) {
                sDmg = GDMsgFormatter.getString(GDMsgFormatter.rbGD, "OFF_COLD");
            }
            if (maxDmgStat.getStatType().equals("offensiveBaseCold")) {
                sDmg = GDMsgFormatter.getString(GDMsgFormatter.rbGD, "OFF_COLD");
            }
            if (maxDmgStat.getStatType().equals("offensiveElemental")) {
                sDmg = GDMsgFormatter.getString(GDMsgFormatter.rbGD, "OFF_ELEMENTAL");
            }
            if (maxDmgStat.getStatType().equals("offensiveFire")) {
                sDmg = GDMsgFormatter.getString(GDMsgFormatter.rbGD, "OFF_FIRE");
            }
            if (maxDmgStat.getStatType().equals("offensiveBaseFire")) {
                sDmg = GDMsgFormatter.getString(GDMsgFormatter.rbGD, "OFF_FIRE");
            }
            if (maxDmgStat.getStatType().equals("offensiveLife")) {
                sDmg = GDMsgFormatter.getString(GDMsgFormatter.rbGD, "OFF_LIFE");
            }
            if (maxDmgStat.getStatType().equals("offensiveBaseLife")) {
                sDmg = GDMsgFormatter.getString(GDMsgFormatter.rbGD, "OFF_LIFE");
            }
            if (maxDmgStat.getStatType().equals("offensiveLightning")) {
                sDmg = GDMsgFormatter.getString(GDMsgFormatter.rbGD, "OFF_LIGHTNING");
            }
            if (maxDmgStat.getStatType().equals("offensiveBaseLightning")) {
                sDmg = GDMsgFormatter.getString(GDMsgFormatter.rbGD, "OFF_LIGHTNING");
            }
            if (maxDmgStat.getStatType().equals("offensivePhysical")) {
                sDmg = GDMsgFormatter.getString(GDMsgFormatter.rbGD, "OFF_PHYSICAL");
            }
            if (maxDmgStat.getStatType().equals("offensiveBonusPhysical")) {
                sDmg = GDMsgFormatter.getString(GDMsgFormatter.rbGD, "OFF_PHYSICAL");
            }
            if (maxDmgStat.getStatType().equals("offensivePierce")) {
                sDmg = GDMsgFormatter.getString(GDMsgFormatter.rbGD, "OFF_PIERCE");
            }
            if (maxDmgStat.getStatType().equals("offensivePoison")) {
                sDmg = GDMsgFormatter.getString(GDMsgFormatter.rbGD, "OFF_POISON");
            }
            if (maxDmgStat.getStatType().equals("offensiveBasePoison")) {
                sDmg = GDMsgFormatter.getString(GDMsgFormatter.rbGD, "OFF_POISON");
            }
            return sDmg;
        }
        return null;
    }

    public static DBItem get(final String itemID) {
        DBItem item = null;
        item = DBItem.hashBuffer.get(itemID);
        if (item == null) {
            item = getDB(itemID);
        }
        return item;
    }

    private static DBItem getDB(final String itemID) {
        DBItem item = null;
        final String command = "SELECT * FROM GD_ITEM WHERE ITEM_ID = ?";
        try (final Connection conn = GDDBData.getConnection();
             final PreparedStatement ps = conn.prepareStatement(command)) {
            ps.setString(1, itemID);
            try (final ResultSet rs = ps.executeQuery()) {
                final List<DBItem> list = wrap(rs);
                if (list.isEmpty()) {
                    item = null;
                } else {
                    item = list.get(0);
                }
                conn.commit();
            } catch (SQLException ex) {
                throw ex;
            }
        } catch (SQLException ex2) {
            final Object[] args = {itemID, "GD_ITEM"};
            final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_READ_TABLE_BY_ID", args);
            GDMsgLogger.addError(msg);
            GDMsgLogger.addError(ex2);
        }
        return item;
    }

    private static SetInfo getSetInfo(final String itemID) {
        SetInfo info = null;
        final String command = "SELECT ITEM_ID, RARITY, REQ_LEVEL FROM GD_ITEM WHERE ITEM_ID = ?";
        try (final Connection conn = GDDBData.getConnection();
             final PreparedStatement ps = conn.prepareStatement(command)) {
            ps.setString(1, itemID);
            try (final ResultSet rs = ps.executeQuery()) {
                final List<SetInfo> list = wrapSetInfo(rs);
                if (list.isEmpty()) {
                    info = null;
                } else {
                    info = list.get(0);
                }
                conn.commit();
            } catch (SQLException ex) {
                throw ex;
            }
        } catch (SQLException ex2) {
            final Object[] args = {itemID, "GD_ITEM"};
            final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_READ_TABLE_BY_ID", args);
            GDMsgLogger.addError(msg);
            GDMsgLogger.addError(ex2);
        }
        return info;
    }

    private void setItemNameTag(final String itemNameTag) {
        String s = GDStashFrame.arcList.getTag(ARCDecompress.FileModule.All, GDConstants.TAG_TEXT_ITEMS, itemNameTag, false);
        if (s == null) {
            s = GDStashFrame.arcList.getTag(ARCDecompress.FileModule.All, GDConstants.TAG_TEXT_STORY, itemNameTag, false);
        }
        this.genderCode = -1;
        if (s != null && s.startsWith("[")) {
            final String code = s.substring(0, 4);
            s = s.substring(4);
            this.genderCode = ARZDecompress.getGenderCode(code);
        }
        if (this.genderCode == -1) {
            this.genderCode = 0;
        }
        this.name = s;
    }

    private void setQualityTag(final String qualityTag) {
        this.qualityTag = qualityTag;
        if (qualityTag == null) {
            this.qualityText = null;
        } else {
            this.qualityText = GDStashFrame.arcList.getTag(ARCDecompress.FileModule.GD, GDConstants.TAG_TEXT_ITEMS, qualityTag, false);
            if (this.qualityText != null) {
                final String[] genders = ARZDecompress.getGenderTexts(this.qualityText);
                this.qualityText = genders[this.genderCode];
            }
        }
    }

    private void setStyleTag(final String styleTag) {
        this.styleTag = styleTag;
        if (styleTag == null) {
            this.styleText = null;
        } else {
            this.styleText = GDStashFrame.arcList.getTag(ARCDecompress.FileModule.GD, GDConstants.TAG_TEXT_ITEMS, styleTag, false);
            if (this.styleText != null) {
                final String[] genders = ARZDecompress.getGenderTexts(this.styleText);
                this.styleText = genders[this.genderCode];
            }
        }
    }

    private void setItemSetNameTag(final String setNameTag) {
        this.setName = GDStashFrame.arcList.getTag(ARCDecompress.FileModule.All, GDConstants.TAG_TEXT_ITEMS, setNameTag, false);
    }

    public static List<DBItem> getByItemIDs(final List<String> itemIDs) {
        final List<DBItem> list = new LinkedList<DBItem>();
        for (final String itemID : itemIDs) {
            final DBItem item = get(itemID);
            list.add(item);
        }
        return list;
    }

    public static List<GDItem> getGDItemsByItemIDs(final List<String> itemIDs) {
        final List<GDItem> list = new LinkedList<GDItem>();
        for (final String itemID : itemIDs) {
            final DBItem dbi = get(itemID);
            if (dbi != null) {
                list.add(new GDItem(dbi));
            }
        }
        return list;
    }

    private void setShardBitmap(final byte[] shardBitmap) {
        int size = 0;
        if (shardBitmap != null) {
            size = shardBitmap.length;
        }
        if (size >= 32768) {
            final Object[] args = {this.itemID};
            final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_IN_ITEM_SHARD_SIZE", args);
            GDMsgLogger.addWarning(msg);
            return;
        }
        this.shardBitmap = shardBitmap;
    }

    public static List<SetInfo> getSetInfoByItemIDs(final List<String> itemIDs) {
        final List<SetInfo> list = new LinkedList<SetInfo>();
        for (final String itemID : itemIDs) {
            final SetInfo info = getSetInfo(itemID);
            list.add(info);
        }
        return list;
    }

    public static List<DBItem> getAll() {
        List<DBItem> list = new LinkedList<DBItem>();
        final String command = "SELECT * FROM GD_ITEM";
        try (final Connection conn = GDDBData.getConnection();
             final PreparedStatement ps = conn.prepareStatement(command)) {
            try (final ResultSet rs = ps.executeQuery()) {
                list = wrap(rs);
                conn.commit();
            } catch (SQLException ex) {
                throw ex;
            }
        } catch (SQLException ex2) {
            final Object[] args = {"All", "GD_ITEM"};
            final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_READ_TABLE_BY_ID", args);
            GDMsgLogger.addError(msg);
            GDMsgLogger.addError(ex2);
        }
        return list;
    }

    public static List<DBItem> getAllUniques() {
        List<DBItem> list = new LinkedList<DBItem>();
        final String command = "SELECT * FROM GD_ITEM WHERE (RARITY = 'Epic' OR RARITY = 'Legendary') AND (ITEM_CLASS LIKE 'Armor%' OR ITEM_CLASS LIKE 'Weapon%')";
        try (final Connection conn = GDDBData.getConnection();
             final PreparedStatement ps = conn.prepareStatement(command)) {
            try (final ResultSet rs = ps.executeQuery()) {
                list = wrap(rs);
                conn.commit();
            } catch (SQLException ex) {
                throw ex;
            }
        } catch (SQLException ex2) {
            final Object[] args = {"Uniques", "GD_ITEM"};
            final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_READ_TABLE_BY_ID", args);
            GDMsgLogger.addError(msg);
            GDMsgLogger.addError(ex2);
        }
        return list;
    }

    public static List<DBItem> getAllLegendaries() {
        List<DBItem> list = new LinkedList<DBItem>();
        final String command = "SELECT * FROM GD_ITEM WHERE RARITY = 'Legendary' AND (ITEM_CLASS LIKE 'Armor%' OR ITEM_CLASS LIKE 'Weapon%')";
        try (final Connection conn = GDDBData.getConnection();
             final PreparedStatement ps = conn.prepareStatement(command)) {
            try (final ResultSet rs = ps.executeQuery()) {
                list = wrap(rs);
                conn.commit();
            } catch (SQLException ex) {
                throw ex;
            }
        } catch (SQLException ex2) {
            final Object[] args = {"Legendaries", "GD_ITEM"};
            final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_READ_TABLE_BY_ID", args);
            GDMsgLogger.addError(msg);
            GDMsgLogger.addError(ex2);
        }
        return list;
    }

    public static List<DBItem> getAllBlueprints() {
        List<DBItem> list = new LinkedList<DBItem>();
        final String command = "SELECT * FROM GD_ITEM WHERE (RARITY = 'Epic' OR RARITY = 'Legendary') AND ITEM_CLASS = 'ItemArtifactFormula' AND (ITEM_ID NOT LIKE '%/craft_random%' AND  ITEM_ID NOT LIKE '%test%' AND  ITEM_ID NOT LIKE 'records/mccm/%' AND  ITEM_ID NOT LIKE 'records/apoc/items/crafting/blueprints/%' AND  ITEM_ID NOT LIKE 'records/cat/items/crafting/blueprints/%' AND  ITEM_ID NOT LIKE 'records/d2/items/crafting/blueprints/%' AND  ITEM_ID NOT LIKE 'records/d3/items/crafting/blueprints/%' AND  ITEM_ID NOT LIKE 'records/doh/items/crafting/blueprints/%' AND  ITEM_ID NOT LIKE 'records/ncff/items/crafting/blueprints/%' AND  ITEM_ID NOT LIKE 'records/tq/items/crafting/blueprints/%')";
        try (final Connection conn = GDDBData.getConnection();
             final PreparedStatement ps = conn.prepareStatement(command)) {
            try (final ResultSet rs = ps.executeQuery()) {
                list = wrap(rs);
                conn.commit();
            } catch (SQLException ex) {
                throw ex;
            }
        } catch (SQLException ex2) {
            final Object[] args = {"Blueprints", "GD_ITEM"};
            final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_READ_TABLE_BY_ID", args);
            GDMsgLogger.addError(msg);
            GDMsgLogger.addError(ex2);
        }
        return list;
    }

    public static List<DBItem> getAllLegendaryBlueprints() {
        List<DBItem> list = new LinkedList<DBItem>();
        final String command = "SELECT * FROM GD_ITEM WHERE RARITY = 'Legendary' AND ITEM_CLASS = 'ItemArtifactFormula' AND (ITEM_ID NOT LIKE '%/craft_random%' AND  ITEM_ID NOT LIKE '%test%' AND  ITEM_ID NOT LIKE 'records/mccm/%' AND  ITEM_ID NOT LIKE 'records/apoc/items/crafting/blueprints/%' AND  ITEM_ID NOT LIKE 'records/cat/items/crafting/blueprints/%' AND  ITEM_ID NOT LIKE 'records/d2/items/crafting/blueprints/%' AND  ITEM_ID NOT LIKE 'records/d3/items/crafting/blueprints/%' AND  ITEM_ID NOT LIKE 'records/doh/items/crafting/blueprints/%' AND  ITEM_ID NOT LIKE 'records/ncff/items/crafting/blueprints/%' AND  ITEM_ID NOT LIKE 'records/tq/items/crafting/blueprints/%')";
        try (final Connection conn = GDDBData.getConnection();
             final PreparedStatement ps = conn.prepareStatement(command)) {
            try (final ResultSet rs = ps.executeQuery()) {
                list = wrap(rs);
                conn.commit();
            } catch (SQLException ex) {
                throw ex;
            }
        } catch (SQLException ex2) {
            final Object[] args = {"Legendary Blueprints", "GD_ITEM"};
            final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_READ_TABLE_BY_ID", args);
            GDMsgLogger.addError(msg);
            GDMsgLogger.addError(ex2);
        }
        return list;
    }

    public static List<DBItem> getBlueprints() {
        List<DBItem> list = new LinkedList<DBItem>();
        final String command = "SELECT * FROM GD_ITEM WHERE ITEM_CLASS = 'ItemArtifactFormula' AND (ITEM_ID NOT LIKE '%/craft_random%' AND  ITEM_ID NOT LIKE '%test%' AND  ITEM_ID NOT LIKE 'records/mccm/%' AND  ITEM_ID NOT LIKE 'records/apoc/items/crafting/blueprints/%' AND  ITEM_ID NOT LIKE 'records/cat/items/crafting/blueprints/%' AND  ITEM_ID NOT LIKE 'records/d2/items/crafting/blueprints/%' AND  ITEM_ID NOT LIKE 'records/d3/items/crafting/blueprints/%' AND  ITEM_ID NOT LIKE 'records/doh/items/crafting/blueprints/%' AND  ITEM_ID NOT LIKE 'records/ncff/items/crafting/blueprints/%' AND  ITEM_ID NOT LIKE 'records/tq/items/crafting/blueprints/%')";
        try (final Connection conn = GDDBData.getConnection();
             final PreparedStatement ps = conn.prepareStatement(command)) {
            try (final ResultSet rs = ps.executeQuery()) {
                list = wrap(rs);
                conn.commit();
            } catch (SQLException ex) {
                throw ex;
            }
        } catch (SQLException ex2) {
            final Object[] args = {"Blueprints", "GD_ITEM"};
            final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_READ_TABLE_BY_ID", args);
            GDMsgLogger.addError(msg);
            GDMsgLogger.addError(ex2);
        }
        return list;
    }

    private static List<DBItem> getByItemCriteria(final SelectionCriteria criteria) {
        final List<String> itemsAll = new LinkedList<String>();
        List<String> itemIDs = null;
        List<String> itemSets = null;
        itemIDs = ItemIDItemCombination.getItemIDs(criteria, CriteriaCombination.Soulbound.INCLUDED, CriteriaCombination.SC_HC.ALL, false, null);
        itemSets = ItemIDItemSetCombination.getItemIDs(criteria, CriteriaCombination.Soulbound.INCLUDED, CriteriaCombination.SC_HC.ALL, false, null);
        mergeItemIDs(itemsAll, itemIDs);
        mergeItemIDs(itemsAll, itemSets);
        final List<DBItem> listAll = getByItemIDs(itemsAll);
        return listAll;
    }

    public static List<DBItem> getByCriteria(final SelectionCriteria criteria) {
        if (criteria.itemIDs != null && !criteria.itemIDs.isEmpty()) {
            return getByItemIDs(criteria.itemIDs);
        }
        return getByItemCriteria(criteria);
    }

    private static void mergeItemIDs(final List<String> listAll, final List<String> list) {
        if (list == null) {
            return;
        }
        for (final String s : list) {
            boolean found = false;
            for (final String sRec : listAll) {
                if (sRec.equals(s)) {
                    found = true;
                    break;
                }
            }
            if (!found) {
                listAll.add(s);
            }
        }
    }

    private static void addSingleCriteriaCombo(final List<DBItem> listAll, final PreparedStatement ps, final String command, final ItemCriteriaCombination combo) {
        final List<DBItem> list = getBySingleCriteriaCombo(ps, command, combo);
        if (list != null && !list.isEmpty()) {
            listAll.addAll(list);
        }
    }

    private static List<DBItem> getBySingleCriteriaCombo(final PreparedStatement ps, final String command, final ItemCriteriaCombination combo) {
        List<DBItem> list = new LinkedList<DBItem>();
        try {
            combo.fillItemStatement(ps);
            try (final ResultSet rs = ps.executeQuery()) {
                list = wrap(rs);
            } catch (SQLException ex) {
                throw ex;
            }
        } catch (SQLException ex) {
            final Object[] args = {command, combo.determineItemParameters()};
            final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_SELECT_FAILED", args);
            GDMsgLogger.addError(msg);
        }
        return list;
    }

    private static void addItemIDsSingleCriteriaCombo(final List<String> listAll, final PreparedStatement ps, final String command, final ItemCriteriaCombination combo) {
        final List<String> list = getItemIDsBySingleCriteriaCombo(ps, command, combo);
        if (list != null && !list.isEmpty()) {
            listAll.addAll(list);
        }
    }

    private static List<String> getItemIDsBySingleCriteriaCombo(final PreparedStatement ps, final String command, final ItemCriteriaCombination combo) {
        List<String> list = new LinkedList<String>();
        try {
            combo.fillItemStatement(ps);
            try (final ResultSet rs = ps.executeQuery()) {
                list = AbstractItemCombination.wrapString(rs, 1);
            } catch (SQLException ex) {
                throw ex;
            }
        } catch (SQLException ex) {
            final Object[] args = {command, combo.determineItemParameters()};
            final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_SELECT_FAILED", args);
            GDMsgLogger.addError(msg);
        }
        return list;
    }

    private static void addSingleDamageCombo(final List<String> listAll, final PreparedStatement ps, final String command, final StatCriteriaCombination scc) throws SQLException {
        final int nextPos = scc.fillItemIDStatement(ps);
        if (nextPos == -1) {
            return;
        }
        for (final SelectionCriteria.StatInfo info : scc.getStatInfoList()) {
            for (final String statType : info.statTypes) {
                ps.setString(nextPos, statType);
                try (final ResultSet rs = ps.executeQuery()) {
                    final List<String> ids = AbstractItemCombination.wrapString(rs, 1);
                    for (final String id : ids) {
                        boolean found = false;
                        for (final String s : listAll) {
                            if (s.equals(id)) {
                                found = true;
                                break;
                            }
                        }
                        if (!found) {
                            listAll.add(id);
                        }
                    }
                } catch (SQLException ex) {
                    throw ex;
                }
            }
        }
    }

    private static List<DBItem> wrap(final ResultSet rs) throws SQLException {
        final LinkedList<DBItem> list = new LinkedList<DBItem>();
        Blob blob = null;
        while (rs.next()) {
            final DBItem item = new DBItem();
            item.itemID = rs.getString(1);
            final DBItem buff = DBItem.hashBuffer.get(item.itemID);
            if (buff != null) {
                list.add(buff);
            } else {
                item.itemClass = rs.getString(2);
                item.armorClass = rs.getString(3);
                item.artifactClass = rs.getString(4);
                item.meshID = rs.getString(5);
                item.baseTextureID = rs.getString(6);
                item.bumpTextureID = rs.getString(7);
                item.glowTextureID = rs.getString(8);
                item.shaderID = rs.getString(9);
                item.bitmapID = rs.getString(10);
                blob = rs.getBlob(11);
                if (blob == null) {
                    item.bitmap = null;
                } else {
                    item.bitmap = blob.getBytes(1L, (int) blob.length());
                }
                if (item.bitmap != null) {
                    try {
                        item.image = DDSLoader.getImage(item.bitmap);
                    } catch (GDParseException ex) {
                        final Object[] args = {item.itemID};
                        final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_BITMAP_DECODE_FAILED", args);
                        GDMsgLogger.addError(msg);
                        item.image = null;
                    }
                    if (item.image != null) {
                        item.overlayImage = DDSLoader.getScaledImage(item.image, 16, 16);
                    }
                }
                item.shardBitmapID = rs.getString(12);
                blob = rs.getBlob(13);
                if (blob == null) {
                    item.shardBitmap = null;
                } else {
                    item.shardBitmap = blob.getBytes(1L, (int) blob.length());
                }
                if (item.shardBitmap != null) {
                    try {
                        item.shardImage = DDSLoader.getImage(item.shardBitmap);
                    } catch (GDParseException ex) {
                        final Object[] args = {item.itemID};
                        final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_BITMAP_DECODE_FAILED", args);
                        GDMsgLogger.addError(msg);
                        item.shardImage = null;
                    }
                    if (item.shardImage != null) {
                        item.overlayShard = DDSLoader.getScaledImage(item.shardImage, 16, 16);
                    }
                }
                item.genderCode = rs.getInt(14);
                item.name = rs.getString(15);
                item.description = rs.getString(16);
                item.qualityTag = rs.getString(17);
                item.qualityText = rs.getString(18);
                item.styleTag = rs.getString(19);
                item.styleText = rs.getString(20);
                item.nameFull = rs.getString(21);
                item.rarity = rs.getString(22);
                item.setID = rs.getString(23);
                item.setName = rs.getString(24);
                item.bonusAffixSetID = rs.getString(25);
                item.itemSkillID = rs.getString(26);
                item.itemSkillLevel = rs.getInt(27);
                item.petBonusSkillID = rs.getString(28);
                item.controllerID = rs.getString(29);
                item.costFormulaSetID = rs.getString(30);
                item.convertIn = rs.getString(31);
                item.convertOut = rs.getString(32);
                item.convertIn2 = rs.getString(33);
                item.convertOut2 = rs.getString(34);
                item.soulbound = rs.getBoolean(35);
                item.hidePrefix = rs.getBoolean(36);
                item.hideSuffix = rs.getBoolean(37);
                item.questItem = rs.getBoolean(38);
                item.cannotPickup = rs.getBoolean(39);
                item.enemyOnly = rs.getBoolean(40);
                item.itemLevel = rs.getInt(41);
                item.reqLevel = rs.getInt(42);
                item.reqDex = rs.getInt(43);
                item.reqInt = rs.getInt(44);
                item.reqStr = rs.getInt(45);
                item.offensiveChance = rs.getInt(46);
                item.retaliationChance = rs.getInt(47);
                item.plusAllSkills = rs.getInt(48);
                item.componentPieces = rs.getInt(49);
                item.maxStackSize = rs.getInt(50);
                item.scalePercent = rs.getInt(51);
                item.slots.slotAxe1H = rs.getBoolean(52);
                item.slots.slotAxe2H = rs.getBoolean(53);
                item.slots.slotDagger1H = rs.getBoolean(54);
                item.slots.slotMace1H = rs.getBoolean(55);
                item.slots.slotMace2H = rs.getBoolean(56);
                item.slots.slotScepter1H = rs.getBoolean(57);
                item.slots.slotSpear1H = rs.getBoolean(58);
                item.slots.slotSpear2H = rs.getBoolean(59);
                item.slots.slotStaff2H = rs.getBoolean(60);
                item.slots.slotSword1H = rs.getBoolean(61);
                item.slots.slotSword2H = rs.getBoolean(62);
                item.slots.slotRanged1H = rs.getBoolean(63);
                item.slots.slotRanged2H = rs.getBoolean(64);
                item.slots.slotShield = rs.getBoolean(65);
                item.slots.slotOffhand = rs.getBoolean(66);
                item.slots.slotAmulet = rs.getBoolean(67);
                item.slots.slotBelt = rs.getBoolean(68);
                item.slots.slotMedal = rs.getBoolean(69);
                item.slots.slotRing = rs.getBoolean(70);
                item.slots.slotHead = rs.getBoolean(71);
                item.slots.slotShoulders = rs.getBoolean(72);
                item.slots.slotChest = rs.getBoolean(73);
                item.slots.slotHands = rs.getBoolean(74);
                item.slots.slotLegs = rs.getBoolean(75);
                item.slots.slotFeet = rs.getBoolean(76);
                Collections.<DBStat>sort(item.stats = DBStat.getItem(item.itemID));
                final DBStat stat = DBStat.getByType(item.stats, "racialBonusPercentDamage", 1);
                if (stat != null) {
                    item.statBonusRaces = DBStatBonusRace.getItem(item.itemID);
                    item.createBonusRaceStats(stat);
                }
                DBStat.applyAttributeScale(item.stats, item.scalePercent);
                item.bonuses = DBSkillBonus.getItem(item.itemID);
                item.skillModifiers = DBSkillModifier.getItem(item.itemID);
                if (item.setID != null) {
                    item.dbItemSet = DBItemSet.get(item.setID);
                }
                if (item.bonusAffixSetID != null) {
                    item.bonusAffixSet = DBAffixSet.get(item.bonusAffixSetID);
                }
                if (item.itemSkillID != null) {
                    item.dbItemSkill = DBSkill.get(item.itemSkillID);
                }
                if (item.petBonusSkillID != null) {
                    item.dbPetBonusSkill = DBSkill.get(item.petBonusSkillID);
                }
                if (item.controllerID != null) {
                    item.dbController = DBController.get(item.controllerID);
                }
                if (item.costFormulaSetID == null && ItemClass.usesCostFormula(item.itemClass)) {
                    item.costFormulaSetID = "records/game/itemcostformulas.dbr";
                }
                if (item.costFormulaSetID != null) {
                    item.dbFormulaSet = DBFormulaSet.get(item.costFormulaSetID);
                    if (item.dbFormulaSet != null) {
                        item.determineStatRequirements();
                    }
                }
                if (!(item.craftable = DBLootTableItemAlloc.isCraftableItemID(item.itemID))) {
                    item.craftable = DBItemCraft.isCraftableItemID(item.itemID);
                }
                item.faction = DBFaction.getFactionTextByItemID(item.itemID);
                if (item.name != null) {
                    final ByteBuffer buffer = StandardCharsets.UTF_8.encode(item.name);
                    final String utf8 = StandardCharsets.UTF_8.decode(buffer).toString();
                    item.name = utf8;
                }
                if (item.description != null) {
                    final ByteBuffer buffer = StandardCharsets.UTF_8.encode(item.description);
                    final String utf8 = StandardCharsets.UTF_8.decode(buffer).toString();
                    item.description = utf8;
                }
                if (item.qualityText != null) {
                    final ByteBuffer buffer = StandardCharsets.UTF_8.encode(item.qualityText);
                    final String utf8 = StandardCharsets.UTF_8.decode(buffer).toString();
                    item.qualityText = utf8;
                }
                if (item.styleText != null) {
                    final ByteBuffer buffer = StandardCharsets.UTF_8.encode(item.styleText);
                    final String utf8 = StandardCharsets.UTF_8.decode(buffer).toString();
                    item.styleText = utf8;
                }
                if (item.nameFull != null) {
                    final ByteBuffer buffer = StandardCharsets.UTF_8.encode(item.nameFull);
                    final String utf8 = StandardCharsets.UTF_8.decode(buffer).toString();
                    item.nameFull = utf8;
                }
                list.add(item);
                DBItem.hashBuffer.put(item.itemID, item);
            }
        }
        return list;
    }

    private static List<SetInfo> wrapSetInfo(final ResultSet rs) throws SQLException {
        final LinkedList<SetInfo> list = new LinkedList<SetInfo>();
        while (rs.next()) {
            final SetInfo info = new SetInfo();
            info.itemID = rs.getString(1);
            info.rarity = rs.getString(2);
            info.level = rs.getInt(3);
            list.add(info);
        }
        return list;
    }

    public static List<ImageInfo> getImageInfos(final String id) {
        final List<ImageInfo> list = new LinkedList<ImageInfo>();
        Blob blob = null;
        final String command = "SELECT ITEM_ID, BITMAP_ID, BITMAP, SHARD_BITMAP_ID, SHARD_BITMAP FROM GD_ITEM WHERE ((BITMAP_ID LIKE '" + id + "%' AND BITMAP IS NULL) OR (SHARD_BITMAP_ID LIKE '" + id + "%' AND SHARD_BITMAP IS NULL))";
        try (final Connection conn = GDDBData.getConnection();
             final PreparedStatement ps = conn.prepareStatement(command)) {
            try (final ResultSet rs = ps.executeQuery()) {
                while (rs.next()) {
                    final ImageInfo info = new ImageInfo();
                    info.itemID = rs.getString(1);
                    info.bitmapID = rs.getString(2);
                    blob = rs.getBlob(3);
                    if (blob == null) {
                        info.bitmap = null;
                    } else {
                        info.bitmap = blob.getBytes(1L, (int) blob.length());
                    }
                    info.shardBitmapID = rs.getString(4);
                    blob = rs.getBlob(5);
                    if (blob == null) {
                        info.shardBitmap = null;
                    } else {
                        info.shardBitmap = blob.getBytes(1L, (int) blob.length());
                    }
                    list.add(info);
                }
                conn.commit();
            } catch (Exception ex) {
                throw ex;
            }
        } catch (Exception ex2) {
            GDMsgLogger.addError(ex2);
        }
        return list;
    }

    public static void deleteNoImageItems() {
        final String command = "DELETE FROM GD_ITEM WHERE BITMAP IS NULL";
        try (final Connection conn = GDDBData.getConnection();
             final PreparedStatement ps = conn.prepareStatement(command)) {
            ps.execute();
            conn.commit();
        } catch (Exception ex) {
            GDMsgLogger.addError(ex);
        }
    }

    public static void updateImageInfo(final List<ImageInfo> list) {
        if (list == null) {
            return;
        }
        final String command = "UPDATE GD_ITEM SET BITMAP = ?, SHARD_BITMAP = ? WHERE ITEM_ID = ?";
        try (final Connection conn = GDDBData.getConnection()) {
            final boolean auto = conn.getAutoCommit();
            conn.setAutoCommit(false);
            for (final ImageInfo info : list) {
                try (final PreparedStatement ps = conn.prepareStatement(command)) {
                    if (info.bitmap != null || info.shardBitmap != null) {
                        if (info.bitmap == null) {
                            ps.setBlob(1, (Blob) null);
                        } else {
                            ps.setBlob(1, new ByteArrayInputStream(info.bitmap));
                        }
                        if (info.shardBitmap == null) {
                            ps.setBlob(2, (Blob) null);
                        } else {
                            ps.setBlob(2, new ByteArrayInputStream(info.shardBitmap));
                        }
                        ps.setString(3, info.itemID);
                        ps.executeUpdate();
                        ps.close();
                        conn.commit();
                    }
                } catch (SQLException ex) {
                    conn.rollback();
                    final Object[] args = {info.itemID};
                    final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_IN_ITEM_IMAGE_SIZE", args);
                    GDMsgLogger.addWarning(msg);
                    GDMsgLogger.addWarning(ex);
                }
            }
        } catch (SQLException ex2) {
            GDMsgLogger.addError(ex2);
        }
    }

    public List<DBAffix> getBonusAffixList() {
        final List<DBAffix> list = new LinkedList<DBAffix>();
        if (this.bonusAffixSet == null) {
            return list;
        }
        return this.bonusAffixSet.getAffixList();
    }

    public boolean isSlotSpear2H() {
        return this.slots.slotSpear2H;
    }

    private String appendNamePart(String s, final int part, final boolean withDmg) {
        switch (part) {
            case 2: {
                if (this.qualityText != null) {
                    if (!s.isEmpty()) {
                        s += " ";
                    }
                    s += this.qualityText;
                    break;
                }
                break;
            }
            case 3: {
                if (this.styleText != null) {
                    if (!s.isEmpty()) {
                        s += " ";
                    }
                    s += this.styleText;
                    break;
                }
                break;
            }
            case 4: {
                if (this.name == null) {
                    break;
                }
                if (!s.isEmpty()) {
                    s += " ";
                }
                s += this.name;
                if (!withDmg) {
                    break;
                }
                final String dmg = this.getMainDamageType();
                if (dmg != null) {
                    s = s + " [" + dmg + "]";
                    break;
                }
                break;
            }
        }
        if (s != null) {
            final ByteBuffer buffer = StandardCharsets.UTF_8.encode(s);
            final String utf8 = s = StandardCharsets.UTF_8.decode(buffer).toString();
        }
        return s;
    }

    public String getModName() {
        if (this.rarity == null) {
            return null;
        }
        String modName = null;
        if (this.rarity.equals("Epic") || this.rarity.equals("Legendary")) {
            if (this.itemID.startsWith("records/apoc/")) {
                modName = GDMsgFormatter.getString(GDMsgFormatter.rbUI, "TXT_MOD_CAT");
            }
            if (this.itemID.startsWith("records/cat/")) {
                modName = GDMsgFormatter.getString(GDMsgFormatter.rbUI, "TXT_MOD_CAT");
            }
            if (this.itemID.startsWith("records/doh/")) {
                modName = GDMsgFormatter.getString(GDMsgFormatter.rbUI, "TXT_MOD_CAT");
            }
            if (this.itemID.startsWith("records/d2/")) {
                modName = GDMsgFormatter.getString(GDMsgFormatter.rbUI, "TXT_MOD_D2");
            }
            if (this.itemID.startsWith("records/d3/")) {
                modName = GDMsgFormatter.getString(GDMsgFormatter.rbUI, "TXT_MOD_D3");
            }
            if (this.itemID.startsWith("records/tq/")) {
                modName = GDMsgFormatter.getString(GDMsgFormatter.rbUI, "TXT_MOD_GQ");
            }
            if (this.itemID.startsWith("records/ncff/")) {
                modName = GDMsgFormatter.getString(GDMsgFormatter.rbUI, "TXT_MOD_NCFF");
            }
            if (this.itemID.startsWith("records/zen/")) {
                modName = GDMsgFormatter.getString(GDMsgFormatter.rbUI, "TXT_MOD_ZEN");
            }
            if (modName != null) {
                modName = "[" + modName + "]";
            }
        }
        if (modName != null) {
            final ByteBuffer buffer = StandardCharsets.UTF_8.encode(modName);
            final String utf8 = modName = StandardCharsets.UTF_8.decode(buffer).toString();
        }
        return modName;
    }

    private void createBonusRaceStats(final DBStat stat) {
        if (stat == null) {
            return;
        }
        if (this.statBonusRaces == null) {
            return;
        }
        if (this.statBonusRaces.isEmpty()) {
            return;
        }
        this.stats.remove(stat);
        final List<DBStat> list = DBStat.createStatsFromRaceBonusList(stat, this.statBonusRaces);
        this.stats.addAll(list);
    }

    private String combineName(final boolean withDmg) {
        String fullname = "";
        fullname = this.appendNamePart(fullname, GDStashFrame.dbConfig.namePart1, withDmg);
        fullname = this.appendNamePart(fullname, GDStashFrame.dbConfig.namePart2, withDmg);
        fullname = this.appendNamePart(fullname, GDStashFrame.dbConfig.namePart3, withDmg);
        fullname = this.appendNamePart(fullname, GDStashFrame.dbConfig.namePart4, withDmg);
        fullname = this.appendNamePart(fullname, GDStashFrame.dbConfig.namePart5, withDmg);
        final String modName = this.getModName();
        if (modName != null) {
            fullname = fullname + " " + modName;
        }
        if (fullname != null) {
            final ByteBuffer buffer = StandardCharsets.UTF_8.encode(fullname);
            final String utf8 = fullname = StandardCharsets.UTF_8.decode(buffer).toString();
        }
        return fullname;
    }

    private void setItemClass(final String itemClass, final String filename, final String dlc) {
        this.itemClass = itemClass;
        if (filename.equals("records/items/misc/inventorybag.dbr")) {
            this.itemClass = "QuestItem";
        }
        if (itemClass.equals("QuestItem")) {
            this.setQuestItem(true);
        }
        if (filename.equals("records/items/crafting/consumables/xppotion_malmouth.dbr")) {
            this.itemClass = "ItemDevotionReset";
        }
        if (filename.equals("records/items/transmutes/dlc_medal_backer.dbr") || filename.equals("records/items/transmutes/dlc_medal_ks_officer.dbr") || filename.equals("records/items/transmutes/dlc_medal_ks_service.dbr") || filename.equals("records/items/transmutes/dlc_medal_ks_veteran.dbr") || filename.equals("records/items/transmutes/transmute_medal_backer.dbr") || filename.equals("records/items/transmutes/transmute_medal_ks_officer.dbr") || filename.equals("records/items/transmutes/transmute_medal_ks_service.dbr") || filename.equals("records/items/transmutes/transmute_medal_ks_veteran.dbr")) {
            this.itemClass = "Kickstarter";
        }
        if (filename.equals("records/items/transmutes/invisible_head.dbr") || filename.equals("records/items/transmutes/invisible_medal.dbr") || filename.equals("records/items/transmutes/invisible_shoulders.dbr") || filename.equals("records/items/transmutes/transmute_invisible_head.dbr") || filename.equals("records/items/transmutes/transmute_invisible_medal.dbr") || filename.equals("records/items/transmutes/transmute_invisible_shoulders.dbr") || filename.equals("records/items/transmutes/transmute_removal.dbr")) {
            this.itemClass = "Kickstarter";
        }
        if (filename.equals("records/items/bonusitems/bonus_burrwitchbrew.dbr") || filename.equals("records/items/bonusitems/bonus_summonwisp.dbr") || filename.equals("records/items/transmutes/dlc_head_admiralhat.dbr") || filename.equals("records/items/transmutes/dlc_head_pillager.dbr") || filename.equals("records/items/transmutes/dlc_head_powderedwig.dbr") || filename.equals("records/items/transmutes/dlc_head_samurai.dbr") || filename.equals("records/items/transmutes/dlc_head_warhelm.dbr") || filename.equals("records/items/transmutes/dlc_torso_swashbucklerscoat.dbr") || filename.equals("records/items/transmutes/transmute_admiralhat.dbr") || filename.equals("records/items/transmutes/transmute_pillagerhelm.dbr") || filename.equals("records/items/transmutes/transmute_powderedwig.dbr") || filename.equals("records/items/transmutes/transmute_samuraihelm.dbr") || filename.equals("records/items/transmutes/transmute_swashbucklerscoat.dbr") || filename.equals("records/items/transmutes/transmute_warhelm.dbr")) {
            this.itemClass = "Kickstarter";
        }
        if (filename.equals("records/items/bonusitems/bonus_summonbat.dbr") || filename.equals("records/items/bonusitems/bonus_summoncrate.dbr") || filename.equals("records/items/bonusitems/bonus_summonowl.dbr") || filename.startsWith("records/items/transmutes/dragongeneral/") || filename.startsWith("records/items/transmutes/greatwolf/") || filename.startsWith("records/items/transmutes/knight/") || filename.startsWith("records/items/transmutes/wizard/") || filename.equals("records/items/transmutes/transmute_blackknight_set.dbr") || filename.equals("records/items/transmutes/transmute_darkwizard_set.dbr") || filename.equals("records/items/transmutes/transmute_dragongeneral_set.dbr") || filename.equals("records/items/transmutes/transmute_greatwolf_set.dbr") || filename.equals("records/items/transmutes/transmute_silverknight_set.dbr") || filename.equals("records/items/transmutes/transmute_wizard_set.dbr")) {
            this.itemClass = "Kickstarter";
        }
        if (filename.startsWith("records/items/transmutes/loyalistpack03set") || filename.startsWith("records/items/transmutes/transmute_loyalistpack03_set")) {
            this.itemClass = "Kickstarter";
        }
        if (filename.startsWith("records/items/bonusitems/") || filename.startsWith("records/items/transmutes/")) {
            this.itemClass = "Kickstarter";
        }
        if (dlc != null) {
            this.itemClass = "Kickstarter";
        }
    }

    private void setQuestItem(final boolean questItem) {
        if (!this.questItem) {
            this.questItem = questItem;
        }
    }

    private void setCraftID(final String craftID) {
        this.dbCraft.setCraftID(craftID);
    }

    private void setCostFormulaSetID(String costFormulaSetID) {
        this.costFormulaSetID = costFormulaSetID;
        if (ItemClass.usesCostFormula(this.itemClass)) {
            if (costFormulaSetID == null) {
                costFormulaSetID = "records/game/itemcostformulas.dbr";
            }
            this.dbFormulaSet = ARZFormulaSetPool.getFormulaSet(costFormulaSetID);
        } else {
            this.dbFormulaSet = null;
        }
        if (this.dbFormulaSet != null) {
            this.determineStatRequirements();
        }
    }

    private void setBitmap(final byte[] bitmap) {
        int size = 0;
        if (bitmap != null) {
            size = bitmap.length;
        }
        if (size >= 131072) {
            final Object[] args = {this.itemID};
            final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_IN_ITEM_IMAGE_SIZE", args);
            GDMsgLogger.addWarning(msg);
            return;
        }
        this.bitmap = bitmap;
    }

    public static class ImageInfo {
        public String itemID;
        public String bitmapID;
        public String shardBitmapID;
        public byte[] bitmap;
        public byte[] shardBitmap;
    }

    public static class SetInfo {
        public String itemID;
        public String rarity;
        public int level;
    }
}
